package fun.tan90.trace;


import lombok.Data;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Data
public class GeneralTrainData {

    private Date ts;

    /**
     * 经度
     */
    private Double longitude;

    /**
     * 纬度
     */
    private Double latitude;

    /**
     * 航向 ° (真北)
     */
    private Integer headingT;

    /**
     * 航向 ° (磁北)
     */
    private Integer headingM;

    /**
     * 航速
     */
    private Double speed;

    /**
     * 标准船速
     */
    private Double standardSpeed;

    /**
     * 自定义标准船速
     */
    private Double customStandardSpeed;

    /**
     * 角度c
     */
    private Integer angle;

    /**
     * VMG
     */
    private Double vmg;

    /**
     * 标准VMG
     */
    private Double standardVmg;

    /**
     * 自定义角度c
     */
    private Integer customAngle;

    /**
     * 自定义风VMG
     */
    private Double customVmg;

    /**
     * 自定义标准VMG
     */
    private Double customStandardVmg;

    /**
     * 风数据来源
     */
    private String windSourceBoatCode;

    /**
     * 平均风速
     */
    private Double windSpeed;

    /**
     * 平均风向
     */
    private Integer windDirection;

    /**
     * 自定义风速
     */
    private Double customWindSpeed;

    /**
     * 自定义风向
     */
    private Integer customWindDirection;

    /**
     * 船首向
     */
    private Integer yaw;

    /**
     * 平衡
     */
    private Integer rol;

    /**
     * 俯仰角
     */
    private Integer pitch;

    /**
     * 舵手心率
     */
    private List<Integer> hr;

    /**
     * 航行状态
     */
    private String sailStatus;

    /**
     * 漂角
     */
    private String leeway;

    /**
     * 航行姿态
     */
    private String posture;

    /**
     * 俯仰姿态
     */
    private String pitchPosture;

    /**
     * 平衡偏离阈值
     */
    private transient final int rolBalanceDeviation;

    /**
     * 俯仰角偏离阈值
     */
    private transient final int pitchBalanceDeviation;

    private transient final BiFunction<String, Double, double[]> standardFun;

    public GeneralTrainData(Date ts,
                            int hrCount,
                            int rolBalanceDeviation,
                            int pitchBalanceDeviation,
                            BiFunction<String, Double, double[]> standardFun) {
        this.ts = ts;
        this.hr = IntStream.range(0, hrCount).mapToObj(n -> -1).collect(Collectors.toList());
        this.rolBalanceDeviation = rolBalanceDeviation;
        this.pitchBalanceDeviation = pitchBalanceDeviation;
        this.standardFun = standardFun;
    }

    public void compute() {
        /*
         * 角度和VMG
         * ● | headingM-windDirection | ≤ 180°，angle = | headingM-windDirection |；
         * ● | headingM-windDirection | ＞ 180°，angle= 360° - | headingM-windDirection |；
         * ● vmg=实时船速*COS(angle)
         * 自定义角度和自定义VMG
         * ● | headingM-customWindDirection | ≤ 180°，customAngle = | headingM-customWindDirection |；
         * ● | headingM-customWindDirection | ＞ 180°，customAngle = 360° - | headingM-customWindDirection |；
         * ● customVmg=实时船速*COS(customAngle)
         */
        if (Objects.nonNull(headingM) && Objects.nonNull(windDirection)) {
            int a = this.headingM - this.windDirection;

            int absAngle = Math.abs(a);
            this.angle = absAngle <= 180 ? absAngle : 360 - absAngle;
            if (Objects.nonNull(speed)) {
                this.vmg = BigDecimal.valueOf(speed * Math.cos(Math.toRadians(angle))).setScale(4, RoundingMode.HALF_UP).doubleValue();
            } else {
                this.vmg = null;
            }

            /*
             * 航行状态(sailStatus)
             * A= headingM - windDirection
             * ● 顺风左舷（D_P）：当 90° ＜ A ≤ 180°，或 -270° ＜ A ＜ -180°；
             * ● 顺风右舷（D_S）：当 -180° ＜ A＜ -90°，或 180° ＜ A ＜ 270°；
             * ● 迎风左舷（U_P）：当 0° ≤ A ≤ 90°，或 -360° ＜ A ≤ -270°；
             * ● 迎风右舷（U_S）：当 270° ≤ A ＜ 360°，或 -90° ≤ A＜ 0°；
             */
            if ((90 < a && a <= 180) || (-270 < a && a < -180)) {
                // 顺风左舷
                this.sailStatus = "D_P";
            } else if ((-180 < a && a < -90) || (180 < a && a < 270)) {
                // 顺风右舷
                this.sailStatus = "D_S";
            } else if ((0 <= a && a <= 90) || (-360 < a && a <= -270)) {
                // 迎风左舷
                this.sailStatus = "U_P";
            } else if ((270 <= a && a < 360) || (-90 <= a && a < 0)) {
                // 迎风右舷
                this.sailStatus = "U_S";
            } else {
                this.sailStatus = null;
            }
            if (this.sailStatus != null) {
                if ("U_S".equals(sailStatus) || "U_P".equals(sailStatus)) {
                    if (Objects.nonNull(windSpeed)) {
                        if (Objects.isNull(this.standardFun)) {
                            this.standardSpeed = null;
                            this.standardVmg = null;
                        } else {
                            double[] standard = this.standardFun.apply("U", windSpeed);
                            this.standardSpeed = standard[0];
                            this.standardVmg = standard[1];
                        }
                    } else {
                        this.standardSpeed = null;
                        this.standardVmg = null;
                    }
                    if (Objects.nonNull(customWindSpeed)) {
                        if (Objects.isNull(this.standardFun)) {
                            this.customStandardSpeed = null;
                            this.customStandardVmg = null;
                        } else {
                            double[] customStandard = this.standardFun.apply("U", customWindSpeed);
                            this.customStandardSpeed = customStandard[0];
                            this.customStandardVmg = customStandard[1];
                        }
                    } else {
                        this.customStandardSpeed = null;
                        this.customStandardVmg = null;
                    }
                } else {
                    if (Objects.nonNull(windSpeed)) {
                        if (Objects.isNull(this.standardFun)) {
                            this.standardSpeed = null;
                            this.standardVmg = null;
                        } else {
                            double[] standard = this.standardFun.apply("D", windSpeed);
                            this.standardSpeed = standard[0];
                            this.standardVmg = standard[1];
                        }
                    } else {
                        this.standardSpeed = null;
                        this.standardVmg = null;
                    }
                    if (Objects.nonNull(customWindSpeed)) {
                        if (Objects.isNull(this.standardFun)) {
                            this.customStandardSpeed = null;
                            this.customStandardVmg = null;
                        } else {
                            double[] customStandard = this.standardFun.apply("D", customWindSpeed);
                            this.customStandardSpeed = customStandard[0];
                            this.customStandardVmg = customStandard[1];
                        }
                    } else {
                        this.customStandardSpeed = null;
                        this.customStandardVmg = null;
                    }
                }
            } else {
                this.standardSpeed = null;
                this.standardVmg = null;
                this.customStandardSpeed = null;
                this.customStandardVmg = null;
            }
        } else {
            this.angle = null;
            this.vmg = null;
            this.sailStatus = null;

            this.standardSpeed = null;
            this.standardVmg = null;
            this.customStandardSpeed = null;
            this.customStandardVmg = null;
        }

        if (Objects.nonNull(headingM) && Objects.nonNull(customWindDirection)) {
            int absCustomAngle = Math.abs(headingM - customWindDirection);
            this.customAngle = absCustomAngle <= 180 ? absCustomAngle : 360 - absCustomAngle;
            if (Objects.nonNull(speed)) {
                this.customVmg = BigDecimal.valueOf(speed * Math.cos(Math.toRadians(customAngle))).setScale(4, RoundingMode.HALF_UP).doubleValue();
            } else {
                this.customVmg = null;
            }
        } else {
            this.customAngle = null;
            this.customVmg = null;
        }

        /*
         * 姿态(posture)
         * ●  当帆船航行状态为U_S和D_S时，rol绝对值小于等于设定阈值时，则帆船平衡，当小于负设定阈值时帆船反扣，当大于正设定阈值度时则帆船倾斜;
         * ● 当帆船航行状态为U_P和D_P时，rol绝对值小于等于设定阈值时，则帆船平衡，当小于负设定阈值时帆船倾斜，当大于正设定阈值度时则帆船反扣;
         */
        if (Objects.nonNull(this.rol)) {
            if (Math.abs(this.rol) <= this.rolBalanceDeviation) {
                this.posture = "平衡";
            } else {
                if (this.sailStatus == null) {
                    this.posture = null;
                } else {
                    if ("U_S".equals(this.sailStatus) || "D_S".equals(this.sailStatus)) {
                        if (this.rol > 0) {
                            this.posture = "倾斜";
                        } else {
                            this.posture = "反扣";
                        }
                    } else {
                        if (this.rol > 0) {
                            this.posture = "反扣";
                        } else {
                            this.posture = "倾斜";
                        }
                    }
                }
            }
        } else {
            this.posture = null;
        }

        if (Objects.nonNull(this.pitch)) {
            if (Math.abs(this.pitch) <= pitchBalanceDeviation) {
                this.pitchPosture = "平衡";
            } else {
                if (this.pitch > 0) {
                    this.pitchPosture = "前仰";
                } else {
                    this.pitchPosture = "后仰";
                }
            }
        } else {
            this.pitchPosture = null;
        }

        /*
         * 漂角(leeway)
         * ● |headingM-yaw|≤90°，w=headingM-yaw;
         * ● headingM-yaw＞90°， w=headingM-yaw-360°;
         * ● headingM-yaw＜-90°，w=360°+headingM-yaw;
         * ● 当姿态为U_P时，如果w>0,  leeway = -w， 如果w<0  leeway = +|w|, 如果w=0，leeway = 0
         * ● 当姿态为U_S时，如果w>0,  leeway = +w，如果w<0  leeway = -|w|, 如果w=0，leeway = 0
         * ● 当姿态为D_P时，如果w>0,  leeway = +w，如果w<0  leeway = -|w|, 如果w=0，leeway = 0
         * ● 当姿态为D_S时，如果w>0,  leeway = +w，如果w<0  leeway = -|w|, 如果w=0，leeway = 0
         */
        if (Objects.nonNull(this.headingM) && Objects.nonNull(this.yaw)) {
            int e = BigDecimal.valueOf(this.headingM - this.yaw).setScale(0, RoundingMode.DOWN).intValue();
            int w;
            if (Math.abs(e) <= 90) {
                w = e;
            } else if (e > 90) {
                w = e - 360;
            } else {
                w = 360 + e;
            }
            if (w == 0) {
                this.leeway = "0";
            } else {
                if (this.sailStatus == null) {
                    this.leeway = null;
                } else {
                    if ("U_P".equals(this.sailStatus)) {
                        this.leeway = w > 0 ? "-" + w : "+" + Math.abs(w);
                    } else {
                        this.leeway = w > 0 ? "+" + w : "-" + Math.abs(w);
                    }
                }
            }
        } else {
            this.leeway = null;
        }
    }
}
